﻿using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
#if !NET40
using System.Threading.Tasks;
#endif
#if NETSTANDARD2_0
using Microsoft.AspNetCore.Http;
#endif
#if NET40 || NET45
using System.Web;
#endif
namespace WeShare.WeiXin.Common
{
    public static class Utils
    {
        public static T DictionaryToObject<T>(Dictionary<string, string> dic)
        {
            var obj = Activator.CreateInstance<T>();
            var type = typeof(T);
            var prop = type.GetProperties();
            foreach (PropertyInfo info in prop)
            {
                if (info.PropertyType.Name == "Dictionary`2")
                {
                    var tempWhere = dic.Where(d => d.Key.Contains($"{info.Name}_"));
                    var tempDic = new Dictionary<string,string>();
                    foreach (var pair in tempWhere)
                    {
                        tempDic.Add(pair.Key,pair.Value);
                    }
                    info.SetValue(obj,tempDic,null);
                    continue;
                }
                if (!dic.ContainsKey(info.Name))
                {
                    continue;
                }
                if (info.PropertyType.IsEnum)
                {
                    var enumValue = Enum.Parse(info.PropertyType, dic[info.Name]);
                    info.SetValue(obj, enumValue, null);
                    continue;
                }
                info.SetValue(obj, Convert.ChangeType(dic[info.Name], info.PropertyType), null);
            }
            return obj;
        }
        public static int GetTimeStamp()
        {
            return ConvertDateTimeInt(DateTime.Now);
        }
        public static int ConvertDateTimeInt(System.DateTime time)
        {
            var startTime = TimeZone.CurrentTimeZone.
             ToLocalTime(new System.DateTime(1970, 1, 1));
            return (int)(time - startTime).TotalSeconds;
        }

        public static string Sha1(string content)
        {
            try
            {
                SHA1 sha1 = new SHA1CryptoServiceProvider();
                byte[] bytesIn = Encoding.UTF8.GetBytes(content);
                byte[] bytesOut = sha1.ComputeHash(bytesIn);
                sha1.Dispose();
                string result = BitConverter.ToString(bytesOut);
                result = result.Replace("-", "");
                return result;
            }
            catch (Exception ex)
            {
                throw new Exception("SHA1加密出错：" + ex.Message);
            }
        }

        public static DateTime UnixTimeToTime(string timeStamp)
        {
            DateTime dtStart = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
            long lTime = long.Parse(timeStamp + "0000000");
            TimeSpan toNow = new TimeSpan(lTime);
            return dtStart.Add(toNow);
        }
        /// <summary>
        /// 下载文件，如果响应的为文件，则将文件内容写到流中。如果不是文件，则返回响应的字符串
        /// </summary>
        /// <param name="url"></param>
        /// <param name="data"></param>
        /// <param name="stream"></param>
        /// <returns></returns>
        public static string DownLoadByPost(string url, string data, FileStreamInfo stream)
        {
            using (var webclient = new WebClient())
            {
                var retdata = webclient.UploadData(url, "POST", Encoding.UTF8.GetBytes(data));
                var disposition = webclient.ResponseHeaders["Content-disposition"];
                if (!string.IsNullOrEmpty(disposition))
                {
                    stream.FileName = disposition.Split('=')[1].Trim('"');
                    stream.Write(retdata, 0, retdata.Length);
                    return "";
                }
                return Encoding.UTF8.GetString(retdata);
            }
        }

        public static string DownLoadByPostResult(string url, object obj, FileStreamInfo stream)
        {
            //序列化设置
            var setting = new JsonSerializerSettings();
            //解决枚举类型序列化时，被转换成数字的问题
            setting.Converters.Add(new StringEnumConverter());
            setting.NullValueHandling = NullValueHandling.Ignore;
            return Utils.DownLoadByPost(url, JsonConvert.SerializeObject(obj, setting), stream);
        }
        /// <summary>
        /// MD5加密
        /// </summary>
        /// <param name="pwd">要加密的字符串</param>
        /// <param name="encoding">字符编码方法。默认utf-8</param>
        /// <returns>加密后的密文</returns>
        public static string MD5(string pwd, string encoding = "utf-8")
        {
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] data = Encoding.GetEncoding(encoding).GetBytes(pwd);
            byte[] md5data = md5.ComputeHash(data);
            md5.Clear();
            string str = "";
            for (int i = 0; i < md5data.Length; i++)
            {
                str += md5data[i].ToString("x").PadLeft(2, '0');
            }
            return str;
        }

        #region 微信支付相关

        public static string GetPaySign(Dictionary<string, string> dictionary, string key)
        {
            var arr = dictionary.OrderBy(d => d.Key).Where(sw => !string.IsNullOrEmpty(sw.Value)).
                Select(d => string.Format("{0}={1}", d.Key, d.Value)).ToArray();
            string stringA = string.Join("&", arr);
            return MD5(string.Format("{0}&key={1}", stringA, key)).ToUpper();
        }
        /// <summary>
        /// 集合转换成XML
        /// </summary>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static string parseXML(Dictionary<string, string> parameters)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("<xml>");
            var arr = parameters.Select(p => string.Format(Regex.IsMatch(p.Value,
            @"^[0-9.]$") ? "<{0}>{1}</{0}>" : "<{0}><![CDATA[{1}]]></{0}>", p.Key,
            p.Value));
            sb.Append(string.Join("", arr));
            sb.Append("</xml>");
            return sb.ToString();
        }

        public static Dictionary<string, string> EntityToDictionary(object obj, int? index = null)
        {
            var type = obj.GetType();
            var dic = new Dictionary<string, string>();
            var pis = type.GetProperties();
            foreach (var pi in pis)
            {
                //获取属性的值
                var val = pi.GetValue(obj, null);
                //移除值为null，以及字符串类型的值为空字符的。另外，签名字符串本身不参与签名，
                //在验证签名正确性时，需移除sign
                if (val == null || val.ToString() == "" || pi.Name == "sign" ||
              (pi.PropertyType.IsGenericType && Nullable.GetUnderlyingType(pi.PropertyType) == null))
                    continue;
                if (index != null)
                {
                    dic.Add(pi.Name + "_" + index, val.ToString());
                }
                else
                {
                    dic.Add(pi.Name, val.ToString());
                }
            }
            var classlist = pis.Where(p => p.PropertyType.IsGenericType && Nullable.GetUnderlyingType(p.PropertyType) == null);
            foreach (var info in classlist)
            {
                var val = info.GetValue(obj, null);
                if (val == null)
                {
                    continue;
                }
                int count = (int)info.PropertyType.GetProperty("Count").GetValue(val, null);
                if (count > 0)
                {
                    for (int i = 0; i < count; i++)
                    {
                        object ol = info.PropertyType.GetMethod("get_Item").Invoke
                        (val, new object[] { i });
                        var tem = EntityToDictionary(ol, i);//递归调用
                        foreach (var t in tem)
                        {
                            dic.Add(t.Key, t.Value);
                        }
                    }
                }
            }
            return dic;
        }

        public static string GetPaySign(object obj, string key)
        {
            var dic = EntityToDictionary(obj);
            return GetPaySign(dic, key);
        }
        #endregion


        #region Base64编码操作
        /// <summary>
        /// Base64编码
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string GetCoding(string str)
        {
            byte[] bytes = Encoding.Default.GetBytes(str);
            return Convert.ToBase64String(bytes);
        }
        /// <summary>
        /// Base64解码
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string GetDecding(string str)
        {
            byte[] outputb = Convert.FromBase64String(str);
            return Encoding.Default.GetString(outputb);
        }

        public static string GetGuid()
        {
            return Guid.NewGuid().ToString("N");
        }
        #endregion

        /// <summary>
        /// 对称解密使用的算法为 AES-128-CBC，数据采用PKCS#7填充。
        /// </summary>
        /// <param name="input"></param>
        /// <param name="key"></param>
        /// <param name="iv"></param>
        /// <returns></returns>
        public static string Decrypt(string input, string key, string iv)
        {
            RijndaelManaged aes = new RijndaelManaged();
            aes.KeySize = 256;
            aes.BlockSize = 128;
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.None;
            aes.Key = Convert.FromBase64String(key);
            aes.IV = Convert.FromBase64String(iv);
            var decrypt = aes.CreateDecryptor(aes.Key, aes.IV);
            byte[] xBuff = null;
            using (var ms = new MemoryStream())
            {
                using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Write))
                {
                    byte[] xXml = Convert.FromBase64String(input);
                    byte[] msg = new byte[xXml.Length + 32 - xXml.Length % 32];
                    Array.Copy(xXml, msg, xXml.Length);
                    cs.Write(xXml, 0, xXml.Length);
                }
                xBuff = decode2(ms.ToArray());
            }
            return Encoding.UTF8.GetString(xBuff);
        }
        private static byte[] decode2(byte[] decrypted)
        {
            int pad = (int)decrypted[decrypted.Length - 1];
            if (pad < 1 || pad > 32)
            {
                pad = 0;
            }
            byte[] res = new byte[decrypted.Length - pad];
            Array.Copy(decrypted, 0, res, 0, decrypted.Length - pad);
            return res;
        }

        //public static byte[] FormToBytes(List<FormEntity> form)
        //{

        //}
#if NETSTANDARD2_0
        /// <summary>
        /// 获取请求的数据
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public static string GetRequestData(this HttpContext context)
        {
            using (var memoryStream = new MemoryStream())
            {
                context.Request.Body.CopyTo(memoryStream);
                var bytes = memoryStream.GetBuffer();
                return Encoding.UTF8.GetString(bytes);
            }
        }
        public static string QueryValue(this HttpRequest request, string key)
        {
            return request.Query[key].ToString();
        }
        /// <summary>
        /// 是否是get请求
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static bool IsGet(this HttpRequest request)
        {
            return request.Method.ToLower() == "get";
        }
#endif
#if NET45 || NET40
        /// <summary>
        /// 获取当前请求的查询字符串
        /// </summary>
        /// <param name="request"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string QueryValue(this HttpRequest request, string key)
        {
            return request.QueryString[key]?.ToString();
        }
        /// <summary>
        /// 是否是get请求
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public static bool IsGet(this HttpRequest request)
        {
            return request.HttpMethod.ToLower() == "get";
        }
        /// <summary>
        /// 获取请求的数据
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public static string GetRequestData(this HttpContext context)
        {
            using (var stream = context.Request.InputStream)
            {
                using (var reader = new StreamReader(stream,Encoding.UTF8))
                {
                    return reader.ReadToEnd();
                }
            }
        }
#endif
        /// <summary>
        /// 验证消息的确来自微信服务器
        /// </summary>
        /// <param name="signature">微信加密签名，signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。</param>
        /// <param name="timestamp">时间戳</param>
        /// <param name="nonce">随机数</param>
        /// <param name="token"></param>
        /// <returns></returns>
        public static bool ValidUrl(this HttpContext context, string token)
        {
            string signature = context.Request.QueryValue("signature");
            string timestamp = context.Request.QueryValue("timestamp");
            string nonce = context.Request.QueryValue("nonce");
            //1.将参数添加到集合
            var arr = new[] { token, nonce, timestamp };
            //2、字典排序
            Array.Sort(arr);
            //3、拼接成一个字符串
            var str = string.Join("", arr);
            //SHA1加密
            var tempsign = Sha1(str).ToLower();
            if (tempsign == signature)
            {
                return true;
            }
            return false;
        }

#if !NET40
        public static async Task<Dictionary<string, string>> EntityToDictionaryAsync(object obj, int? index = null)
        {
            return await Task.Run(async () =>
            {
                var type = obj.GetType();
                var dic = new Dictionary<string, string>();
                var pis = type.GetProperties();
                foreach (var pi in pis)
                {
                    //获取属性的值
                    var val = pi.GetValue(obj, null);
                    //移除值为null，以及字符串类型的值为空字符的。另外，签名字符串本身不参与签名，
                    //在验证签名正确性时，需移除sign
                    if (val == null || val.ToString() == "" || pi.Name == "sign" ||
                        (pi.PropertyType.IsGenericType && Nullable.GetUnderlyingType(pi.PropertyType) == null))
                        continue;
                    if (index != null)
                    {
                        dic.Add(pi.Name + "_" + index, val.ToString());
                    }
                    else
                    {
                        dic.Add(pi.Name, val.ToString());
                    }
                }

                var classlist = pis.Where(p =>
                    p.PropertyType.IsGenericType && Nullable.GetUnderlyingType(p.PropertyType) == null);
                foreach (var info in classlist)
                {
                    var val = info.GetValue(obj, null);
                    if (val == null)
                    {
                        continue;
                    }

                    int count = (int)info.PropertyType.GetProperty("Count").GetValue(val, null);
                    if (count > 0)
                    {
                        for (int i = 0; i < count; i++)
                        {
                            object ol = info.PropertyType.GetMethod("get_Item").Invoke
                                (val, new object[] { i });
                            var tem = await EntityToDictionaryAsync(ol, i); //递归调用
                            foreach (var t in tem)
                            {
                                dic.Add(t.Key, t.Value);
                            }
                        }
                    }
                }
                return dic;
            });
        }
#endif
    }
}
